/**
 * Copyright (C)  Gadglet .
 *
 * This file is part of Gadglet
 *
 * Gadglet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Gadglet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Gadglet. If not, see <http://www.gnu.org/licenses/>.
 */
package com.gadglet.servlets;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.oauth.OAuth;
import net.oauth.OAuthAccessor;
import net.oauth.OAuthConsumer;
import net.oauth.OAuthException;
import net.oauth.OAuthMessage;
import net.oauth.OAuthProblemException;
import net.oauth.SimpleOAuthValidator;
import net.oauth.signature.RSA_SHA1;

import com.gadglet.core.BasicRequestHandler;
import com.gadglet.core.GadgletRequestWrapper;
import com.gadglet.core.GadgletResponse;
import com.gadglet.core.RequestException;
import com.gadglet.core.RequestHandlerFactory;
import com.gadglet.data.Gadget;
import com.gadglet.params.ReqActionTypes;
import com.gadglet.params.ReqErrorTypes;
import com.gadglet.params.SharedConstants;
import com.google.gson.Gson;


/**
 * Gadglet basic servlet, provides some utilities.
 */

public class BasicRequestServlet extends HttpServlet {


	protected String domainName = null;
	protected String gadgetsRootPackageName = null;
	protected String userRegistrationMethod = null;
	
	  @Override
	public void init(ServletConfig config) throws ServletException {
	    super.init(config);
	    ServletContext context = getServletContext();
	    domainName = context.getInitParameter(SharedConstants.ctxParamServingDomain);
	    gadgetsRootPackageName = context.getInitParameter(SharedConstants.ctxParamDefaultGadgetsPackage);
	    
	    if (context.getInitParameter(SharedConstants.ctxParamRegMethod) == null || !context.getInitParameter(SharedConstants.ctxParamRegMethod).equalsIgnoreCase(SharedConstants.registrationMethodSigned))
	    	userRegistrationMethod = SharedConstants.registrationMethodDual ;
	    else
	    	userRegistrationMethod = SharedConstants.registrationMethodSigned;
	    

	  }
	  
	protected static final long serialVersionUID = -8483581882374689032L;
	private Logger log = Logger.getLogger(this.getClass().getName());
	protected static CertificateProvider certificateProvider = CertificateProvider
			.get();

	
	
	/**
	 * Set JSON headers
	 * @param response
	 */
	protected void setResponseHeaders(HttpServletResponse response) {
		response.setContentType("text/x-json;charset=UTF-8");
		response.setHeader("Cache-Control", "no-cache");
	}

	/**
	 * Enables printStackTrace to a log 
	 * @param e - exception
	 */
	protected void printStackTrace(Exception e) {
		StackTraceElement[] stackTrace = e.getStackTrace();
		if (stackTrace != null) {
			final String newLine = System.getProperty("line.separator");
			// final String headerLine =
			// "-----------------------------------------------------------------------";
			final String headerTitlePortion = "-- StackTraceElement Index #";
			int index = 0;
			StringBuffer msg = new StringBuffer();
			msg.append(e.getMessage() + newLine);
			for (StackTraceElement element : stackTrace) {
				final String exceptionMsg = "Exception thrown from "
						+ element.getMethodName() + " in class "
						+ element.getClassName() + " [on line number "
						+ element.getLineNumber() + " of file "
						+ element.getFileName() + "]";

				msg.append((headerTitlePortion + index++ + newLine));
				msg.append((exceptionMsg + newLine + newLine).getBytes()
						.toString());
				msg.append(("Exception.toString: " + element.toString() + newLine));

			}
			log.warning(msg.toString());
		} else
			log.warning(e.getMessage());
	}

	
	/**
	 * Validates opensocial SIGNED requests
	 *  
	 * @param platformRequestWrapper
	 * @return true - valid request, false - not valid request
	 */
	protected boolean validateSignedRequestWithContainer(
			GadgletRequestWrapper platformRequestWrapper) {
		// check if on localhost
		try {
			String consumerKey = platformRequestWrapper
					.getOpenSocialConsumerKey();
			if (consumerKey == null) {

				log.warning("no consumerKey");
				return false;
			}

			String certificate = certificateProvider
					.getCertificate(consumerKey);
			if (certificate == null) {

				log.warning("no certificate found , oauth_consumer_key="
						+ consumerKey);
				return false;
			}

			OAuthConsumer consumer = new OAuthConsumer(null, consumerKey, null,
					null);

			consumer.setProperty(RSA_SHA1.X509_CERTIFICATE, certificate);

			OAuthMessage message = new OAuthMessage(platformRequestWrapper.getMethod(),
					platformRequestWrapper.getRequestURL().toString(),
					getRequestParameters(platformRequestWrapper));

			OAuthAccessor accessor = new OAuthAccessor(consumer);

			SimpleOAuthValidator validator = new SimpleOAuthValidator();
			validator.validateMessage(message, accessor);
			return true;
		} catch (OAuthProblemException ope) {

			log.warning(ope.toString());
		} catch (IOException e) {
			log.warning(e.toString());
		} catch (URISyntaxException e) {
			log.warning(e.toString());
		} catch (OAuthException e) {
			log.warning(e.toString());
		}
		return false;
	}

	

	protected String getRequestUrl(HttpServletRequest request) {
		StringBuilder requestUrl = new StringBuilder();
		String scheme = request.getScheme();
		int port = request.getLocalPort();

		requestUrl.append(scheme);
		requestUrl.append("://");
		requestUrl.append(request.getServerName());

		if ((scheme.equals("http") && port != 80)
				|| (scheme.equals("https") && port != 443)) {
			requestUrl.append(":");
			requestUrl.append(port);
		}

		requestUrl.append(request.getContextPath());
		requestUrl.append(request.getServletPath());

		return requestUrl.toString();
	}

	protected List<OAuth.Parameter> getRequestParameters(
			HttpServletRequest request) {

		List<OAuth.Parameter> parameters = new ArrayList<OAuth.Parameter>();

		for (Object e : request.getParameterMap().entrySet()) {
			Map.Entry<String, String[]> entry = (Map.Entry<String, String[]>) e;

			for (String value : entry.getValue()) {
				parameters.add(new OAuth.Parameter(entry.getKey(), value));
			}
		}

		return parameters;
	}
	
	
	
	/**
	 * Gadglet main controller. 
	 * 
	 * @param request  - wrapper of the HttpRequest
	 * @param gadgletResponse - Gadglet (JSON) response
	 * @param response - HttpResponse
	 */
	protected void performRequest(GadgletRequestWrapper request,
			GadgletResponse gadgletResponse, HttpServletResponse response) {

		
		String gadgetName = null;
		ReqActionTypes actionType = null;
		BasicRequestHandler handler = null;
		String requestHandlerPackageName = null;
		
		gadgletResponse.setUserContainer(request.getOpenSocialConsumerKey());
		
		
		
		try {
			actionType = request.getGadgetActionType();

			gadgetName = request.getGadgetName();
			
			Gadget g = request.getRequestedGadget();
			
			String contentName = request.getContentName();
					
			if(g!=null)
				requestHandlerPackageName = g.getHandlerPackageName();
			
			handler = RequestHandlerFactory.getGadgetRequestHandler(gadgetName,gadgetsRootPackageName,requestHandlerPackageName,contentName);

			if (actionType.equals(ReqActionTypes.CONFIG))
				handler.doConfig(request, gadgletResponse);
			else if (actionType.equals(ReqActionTypes.VIEW))
				handler.doView(request, gadgletResponse);
			else if (actionType.equals(ReqActionTypes.DELETE))
				handler.doDelete(request, gadgletResponse);
			else if (actionType.equals(ReqActionTypes.ADD))
				handler.doAdd(request, gadgletResponse);
			else if (actionType.equals(ReqActionTypes.UPDATE))
				handler.doUpdate(request, gadgletResponse);
			else if (actionType.equals(ReqActionTypes.SEARCH))
				handler.doSearch(request, gadgletResponse);
			else if (actionType.equals(ReqActionTypes.INVITE_FRIEND))
				handler.doInvite(request, gadgletResponse);
			else if (actionType.equals(ReqActionTypes.ACCEPT_FRIEND))
				handler.doAcceptFriend(request, gadgletResponse);
			else if (actionType.equals(ReqActionTypes.REJECT_FRIEND))
				handler.doRejectFriend(request, gadgletResponse);
			else if (actionType.equals(ReqActionTypes.GET_FRIENDS))
				handler.doGetFriends(request, gadgletResponse);
			else if (actionType.equals(ReqActionTypes.GET_DOMAIN_USERS))
				handler.doGetDomainUsers(request, gadgletResponse);
			else if (actionType.equals(ReqActionTypes.GET_INVITATIONS))
				handler.doGetInvitations(request, gadgletResponse);
			else if (actionType.equals(ReqActionTypes.SHARE))
				handler.doShare(request, gadgletResponse);
			else if (actionType.equals(ReqActionTypes.SHARED_LIST))
				handler.doSharedList(request, gadgletResponse);
			else if (actionType.equals(ReqActionTypes.ADD_PROFILE))
				handler.addProfile(request, gadgletResponse);
			else if (actionType.equals(ReqActionTypes.REMOVE_FRIEND))
				handler.doRemoveFriend(request, gadgletResponse);
			else if (actionType.equals(ReqActionTypes.UPDATE_PROFILE))
				handler.updateProfile(request, gadgletResponse);
			else if (actionType.equals(ReqActionTypes.GET_PROFILE))
				handler.getProfile(request, gadgletResponse);
			else if (actionType.equals(ReqActionTypes.CUSTOM) && request.getCustomAction() != null){
				
				// will try custom action, using reflection ......
				
				Class<?> args[] = new Class[2];

				args[0] = GadgletRequestWrapper.class;
				args[1] = GadgletResponse.class;

				Method method = handler.getClass().getMethod(
						request.getCustomAction(), args);
				
				Object[] params = new Object[2];
				params[0] = request;
				params[1] = gadgletResponse;
				method.invoke((Object)handler, (Object[])params);
				
			} 
			else
				throw new RequestException(
						ReqErrorTypes.UNRECOGNIZED_ACTION);

		}

		catch (RequestException e) {
			gadgletResponse.setError(e);
		} catch (Exception e) {
			gadgletResponse.setError(new RequestException(
					ReqErrorTypes.REQUEST_FAILED));

			printStackTrace(e);
		}
		PrintWriter out = null;
		try {
			out = response.getWriter();
			Gson gson = new Gson();
			out.print(gson.toJson(new JsonEnvelope(gadgletResponse)));
		} catch (IOException e) {

			printStackTrace(e);
		}

	}
	
	
	

}
